# $Id: models.py 7557b5d4d82b 2012/12/02 09:07:16 pgurumur $
# Copyright (c) 2009 Prabhu Gurumurthy  <pgurumur@gmail.com>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

__version__ = "$Revision: 7557b5d4d82b $"
__author__ = "$Author: pgurumur $"

import binascii
import datetime
import time

from django.conf import settings
from django.contrib.auth.models import User
from django.db import models

from djanet.db import choices, netdb, query
from djanet.forms.fields import NetworkAddressField
from djanet.lib import crypto, ip, stdlib
from djanet.lib.exception import ModelError
from djanet.utils import utils

class CryptField(models.CharField):
   def __init__(self, *args, **kwargs):
      self._err = ModelError()
      self._log = utils.loghandle(stdlib.my_name(self))

      self._crypt = None
      if "max_length" in kwargs:
         self._log.warning("max length will be overwritten in CryptField")

      kwargs['max_length'] = 255
      if hasattr(settings, 'CRYPTO'):
         algorithm = settings.CRYPTO['ENC'].lower()
         cryptokey = settings.CRYPTO['SECRET']
         try:
            if algorithm == "aes":
               self._crypt = crypto.aes(cryptokey)
            elif algorithm == "3des":
               self._crypt = crypto.tripledes(cryptokey)
            elif algorithm == "blowfish":
               self._crypt = crypto.blowfish(cryptokey)
            else:
               self._crypt = crypto.aes(cryptokey)
         except crypto.AESError, message:
            self._log.notice(message)
            raise ModelError, message
      else:
         raise ModelError, 'crypto key attribute not defined in settings'

      super(CryptField, self).__init__(*args, **kwargs)

   def save_form_data(self, instance, data):
      setattr(instance, self.name,
            binascii.b2a_base64(self._crypt.encrypt(data)))

   def value_from_object(self, obj):
      return self._crypt.decrypt(
            binascii.a2b_base64(getattr(obj, self.attname)))

class NetworkAddressField(models.DecimalField):
   def __init__(self, *args, **kwargs):
      self._err = ModelError()
      self._log = utils.loghandle(stdlib.my_name(self))
      self._version = 0
      kwargs['max_digits'] = 41
      kwargs['decimal_places'] = 0
      super(NetworkAddressField, self).__init__(*args, **kwargs)

   def formfield(self, **kwargs):
      defaults = { 'form_class' : NetworkAddressField }
      defaults.update(kwargs)
      return super(NetworkAddressField, self).formfield(**defaults)

   def _mesgstr(self, Message):
      message = None
      if Message:
         if not isinstance(Message, str):
            message = str(Message)
         else:
            message = Message

      return message

   def _error(self, Message):
      message = self._mesgstr(Message)
      self._log.notice(message)
      raise ModelError, message

   def value_to_string(self, Object):
      if Object:
         address = super(NetworkAddressField, self).value_to_string(Object)
         try:
            return ip.ip(int(address), version = self._version)
         except ip.IPError, message:
            self._error(message)
      else:
         self._error("no object passed to convert the value to string")

class IPAddressField(NetworkAddressField):
   def __init__(self, *Args, **Keywords):
      super(stdlib.my_class(self), self).__init__(*Args, **Keywords)
      self._version = 4

class IPv6AddressField(NetworkAddressField):
   def __init__(self, *Args, **Keywords):
      super(stdlib.my_class(self), self).__init__(*Args, **Keywords)
      self._version = 6

class BaseModel(models.Model):
   updated_user = models.ForeignKey(User, editable = False, null = True,
         blank = True, related_name = "%(class)s_user_id")
   description = models.TextField(null = True, blank = True,
         help_text = "Description for this row in the database",
         name = "description")
   created = models.DateTimeField(auto_now = True, auto_now_add = True,
         help_text = "date and time when created", name = "created_date",
         editable = False, default = datetime.datetime.now)
   updated = models.DateTimeField(auto_now = True, auto_now_add = True,
         help_text = "date and time when last updated",
         name = "last_updated", editable = False,
         default = datetime.datetime.now)
   timezone = models.CharField(max_length = 5, name = "timezone",
         help_text = "Timezone", editable = False)

   def __init__(self, *args, **kwargs):
      self._log = utils.loghandle(stdlib.my_name(self))
      self.created = datetime.datetime.now()
      self._readOnly = False
      if hasattr(settings, 'READ_ONLY') and settings.READ_ONLY:
         self._readOnly = True

      super(BaseModel, self).__init__(*args, **kwargs)

   def debug(self, Message):
      if hasattr(settings, 'MODEL_DEBUG') and settings.MODEL_DEBUG:
         self._log.info(str(Message))

   def save(self, *args, **kwargs):
      self.updated = datetime.datetime.now()
      self.timezone = time.strftime("%Z")
      self.debug("%s updated @ %s by %s" %(
         stdlib.my_name(self), self.UpdatedTime(), self.updated_user))
      if not self._readOnly:
         super(BaseModel, self).save(*args, **kwargs)

   def UpdatedTime(self):
      return "%s %s" %(str(self.updated), str(self.timezone))

   def UpdatedBy(self):
      return str(self._GetUserName())

   def CreatedTime(self):
      return str(self.created)

   def __unicode__(self):
      return unicode(str(self))

   def __repr__(self):
      return repr(str(self))

   def __int__(self):
      return int(self.id)

   class Meta:
      abstract = True

class BaseRouteMap(BaseModel):
   name = models.CharField(max_length = 24, help_text = "Route map name")
   device = models.ForeignKey(netdb.Device)
   sequence = models.IntegerField(
         help_text = "Sequence number of the route map")
   action = models.CharField(max_length = 10,
         choices = choices.ROUTEMAP_CHOICES, help_text = "Route map action")

   class Meta:
      abstract = True

   def __str__(self):
      return stdlib.my_str(self.name)
